package com.alaia.alaiaauth.filter;

import com.alaia.alaiacommoncore.auth.model.LoginAppUser;
import com.alaia.alaiacommoncore.model.SysUser;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    SessionRegistry sessionRegistry;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //首先登录请求肯定是 POST，如果不是 POST ，直接抛出异常，后面的也不处理了
        if (request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        //这里获取存Session中的验证码（注意这里可以降验证码缓存在redis中
        String verify_code = (String)request.getSession().getAttribute("verify_code");
        // 接下来通过 contentType 来判断当前请求是否通过 JSON 来传递参数，
        // 如果是通过 JSON 传递参数，则按照 JSON 的方式解析，如果不是，则调用 super.attemptAuthentication 方法，进入父类的处理逻辑中，
        // 也就是说，我们自定义的这个类，既支持 JSON 形式传递参数，也支持 key/value 形式传递参数。
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            Map<String, String> loginData = new HashMap<>();
            try {
                //如果是 JSON 形式的数据，我们就通过读取 request 中的 I/O 流，将 JSON 映射到一个 Map 上。
                loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                String code = loginData.get("code");
                //这里是去验证一下验证码对不对
                checkCode(response, code, verify_code);
            }
            //这里是固定的使用写好的名称获取，当前也可以配置后自定义
            //Map 中取出 username 和 password，
            String username = loginData.get(getUsernameParameter());
            String password = loginData.get(getPasswordParameter());
            if (username == null) {
                username = "";
            }
            if (password == null) {
                password = "";
            }
            //trim() 方法用于删除字符串的头尾空白符。
            username = username.trim();
            //构造 UsernamePasswordAuthenticationToken
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username,password);
            setDetails(request,usernamePasswordAuthenticationToken);
            //这里做了顶号处理，这样不能同时登陆多人
            LoginAppUser principal = new LoginAppUser();
            SysUser sysUser = new SysUser();
            sysUser.setUserName(username);
            principal.setUser(sysUser);
            sessionRegistry.registerNewSession(request.getSession(true).getId(), principal);
            //对象并作校验。(authenticate)，这里是先将认证处理器的管理者：ProviderManager 放入UsernamePasswordAuthenticationFilter中，这里我们自定义了认证管理
            return this.getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
        }else {
            checkCode(response, request.getParameter("code"), verify_code);
            return super.attemptAuthentication(request, response);
        }
    }

    private void checkCode(HttpServletResponse response, String code, String verify_code) {
        //从 Map 中取出 code，先去判断验证码是否正确，如果验证码有错，则直接抛出异常。验证码的判断逻辑，
        if (code == null || verify_code == null || "".equals(code) || !verify_code.toLowerCase().equals(code.toLowerCase())) {
            //验证码不正确
            throw new AuthenticationServiceException("验证码不正确");
        }
    }
}
